"""
Projects are groups of files with a custom configuration file in the root
directory that stores project information.
"""
import wx, sys, os, wx.html, re, yaml
from util import *

PROJECT_SETTINGS_FILE = 'project_settings.yaml'
UPDATE_INTERVAL = 2000

class Project(object):
	"A collection of files with a title and a Python file to run."
	# TODO: every file we make needs to have a path, or at least
	# some other representation the editors can use.
	# TODO: new files need to be added to projects!
	# TODO: make a project "editor" that can be "saved" like other resources.
	def __init__(self, path, title = None, file_to_run = None,
		         read_config_file = True):
		"""
		Creates a new Project with the given path.
		The other information will be read from the configuration file in
		the path, if the config info exists.
		"""
		self.path = path

		project_settings_file = self._get_project_settings_file()

		self.title       = title or os.path.basename(path)
		self.file_to_run = file_to_run

		# Read info from a settings file
		if read_config_file and os.path.isfile(project_settings_file):
			with open(project_settings_file) as f:
				data = yaml.load(f.read())

			self.title       = data.get('title', self.title)
			self.file_to_run = data.get('file_to_run', self.file_to_run)

		# TODO: add other metadata.

	def _get_project_settings_file(self):
		return os.path.join(self.path, PROJECT_SETTINGS_FILE)

	def save(self):
		"""
		Saves the project metadata.
		"""
		# Write out data to a config file in the path.
		with open(self._get_project_settings_file(), 'w') as f:
			f.write(yaml.dump({
				'title': self.title,
				'file_to_run': self.file_to_run
			}, default_flow_style=False))

# TODO: do the filtering in a better way...
EXCLUSION_RE = re.compile(r'\.|.*\.pyc$|.*\.swp$')

class ProjectViewer(wx.TreeCtrl):
	"Displays the contents of a project in a tree view."
	# TODO: load files in a folder only when the tree node is expanded.
	def __init__(self, parent):
		super(ProjectViewer, self).__init__(parent)
		self.parent = parent
		self.Bind(wx.EVT_LEFT_DCLICK, self.on_left_dclick)
		self.bind_project()
		# TODO: allow title to change after panel is placed.
		wx.CallLater(UPDATE_INTERVAL, self._update_wrapper)

	def _update_wrapper(self):
		self.update_view()
		wx.CallLater(UPDATE_INTERVAL, self._update_wrapper)

	def update_view(self):
		if not self.project: return
		# TODO: detect project folder deletion.
		self._check_item(self.GetRootItem(),
		                 [os.path.basename(self.project.path)])

	def _check_item(self, node, item_list):
		"""
		Checks to see if an item was deleted, added, or renamed.
		 - If this item is in the item_list, it removes itself from the
		   item_list and no changes happen.
		 - If this item is NOT in the item_list, it removes itself from the
		   tree.
		All items left over in the item_list will be passed to _add_item.
		"""
		path = self.GetPyData(node)
		# TODO: might want to cache this type info
		# TODO: detect when a directory changes into a file, and vice
		# versa.
		if os.path.isdir(path):
			inner_item_list = os.listdir(path)
			child, _ = self.GetFirstChild(node)
			while child:
				self._check_item(child, inner_item_list)
				child = self.GetNextSibling(child)

			# Add all new items
			for item in inner_item_list:
				if EXCLUSION_RE.match(item): continue
				print "Will add item %s" % item
				#self._add_item(os.path.join(path, item), item, node)

		base = unicode(os.path.basename(path))
		if base not in item_list:
			self.Delete(node)
		else:
			item_list.remove(base)

	def _add_item(self, path, title, parent_node = None):
		"Adds a project path to the tree"
		if parent_node is None:
			tree_node = self.AddRoot(title)
		else:
			tree_node = self.AppendItem(parent_node, title)
		self.SetPyData(tree_node, path)
		if os.path.isdir(path):
			for child in os.listdir(path):
				if EXCLUSION_RE.match(child): continue
				self._add_item(os.path.join(path, child), child, tree_node)
		else:
			pass
			# TODO: file icon, make clickable.
	
	def bind_project(self, project=None):
		"Binds a project to this tree."
		self.project = project
		self.DeleteAllItems()
		if not project:
			self.AddRoot("No Project Loaded")
		else:
			# Iterate over project items
			self._add_item(os.path.abspath(project.path), project.title)
			self.Expand(self.GetRootItem())

	def on_left_dclick(self, event):
		item, flags = self.HitTest(event.GetPosition())
		if item:
			path = self.GetPyData(item)
			if os.path.isfile(path):
				self.parent.app.open_file(path)
